package lxs.swift.instance.convert;

import lxs.swift.instance.HashStruct;
import lxs.swift.instance.StructArray;
import lxs.swift.instance.Types;
import lxs.swift.tool.Dater;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;


public class DefaultConverter implements Converter {
    private static final Converter CONVERTER = new DefaultConverter();

    private DefaultConverter() {
    }

    public static Converter getInstance() {
        return CONVERTER;
    }

    private static final Function<Object, HashStruct> OBJECT_CONVERTER = Types::toStruct;
    private static final Function<Object, StructArray> ARRAY_CONVERTER = Types::toArray;
    private static final Function<Object, String> STRING_CONVERTER = o -> {
        if (o == null) return null;
        return o.toString();
    };

    private static final Function<Object, Boolean> BOOLEAN_CONVERTER = o -> {
        if (o == null) return null;
        if (o instanceof Boolean) {
            return ((Boolean) o);
        }
        String string = o.toString();
        return Boolean.parseBoolean(string);
    };
    private static final Function<Object, Date> DATE_CONVERTER = o -> {
        if (o == null) return null;
        if (o instanceof Date) return ((Date) o);
        if (o instanceof Byte) {
            return new Date((Byte) o);
        }
        if (o instanceof Short) {
            return new Date((Short) o);
        }
        if (o instanceof Integer) {
            return new Date((Integer) o);
        }
        if (o instanceof Long) {
            return new Date((Long) o);
        }
        if (o instanceof Temporal) {
            Temporal temporal = (Temporal) o;
            LocalDateTime epoch = Instant.EPOCH.atZone(ZoneId.systemDefault()).toLocalDateTime();
            long until = temporal.until(epoch, ChronoUnit.MILLIS);
            return new Date(until);
        }
        if (o instanceof CharSequence) {
            String s = o.toString();
            if (s.matches("[0-9]+")) {
                return new Date(Long.parseLong(s));
            }
            return Dater.parse(s).date;
        }
        throw new RuntimeException("无法转换为时间类型");

    };
    private static final Function<Object, Integer> INT_CONVERTER = o -> {
        if (o == null) return null;
        if (o instanceof Byte) {
            return Integer.valueOf((Byte) o);
        }
        if (o instanceof Short) {
            return Integer.valueOf((Short) o);
        }
        if (o instanceof Integer) {
            return ((Integer) o);
        }
        if (o instanceof Character) {
            Character character = (Character) o;
            if (character >= '0' && character <= '9') {
                return character - '0';
            }
        }
        return Integer.parseInt(o.toString());
    };
    private static final Function<Object, Long> LONG_CONVERTER = o -> {
        if (o == null) return null;
        if (o instanceof Byte) {
            return Long.valueOf((Byte) o);
        }
        if (o instanceof Short) {
            return Long.valueOf((Short) o);
        }
        if (o instanceof Integer) {
            return Long.valueOf((Integer) o);
        }
        if (o instanceof Long) {
            return ((Long) o);
        }
        return Long.parseLong(o.toString());
    };
    private static final Function<Object, Double> DOUBLE_CONVERTER = o -> {
        if (o == null) return null;
        if (o instanceof Byte) {
            return Double.valueOf((Byte) o);
        }
        if (o instanceof Short) {
            return Double.valueOf((Short) o);
        }
        if (o instanceof Integer) {
            return Double.valueOf((Integer) o);
        }
        if (o instanceof Long) {
            return Double.valueOf((Long) o);
        }
        if (o instanceof Float) {
            return Double.valueOf((Float) o);
        }
        if (o instanceof Double) {
            return ((Double) o);
        }
        return Double.parseDouble(o.toString());
    };
    private static final BiFunction<Class<Enum<?>>, Object, Enum<?>> ENUM_CONVERTER = (clazz, o) -> {
        Enum<?>[] enumConstants = clazz.getEnumConstants();
        Map<String, Enum<?>> nameMapEnum = new HashMap<>();
        for (Enum<?> enumConstant : enumConstants) {
            String name = enumConstant.name();
            nameMapEnum.put(name, enumConstant);
        }
        String key = STRING_CONVERTER.apply(o);
        return nameMapEnum.get(key);
    };


    @Override
    public Function<Object, HashStruct> structConverter() {
        return OBJECT_CONVERTER;
    }

    @Override
    public Function<Object, StructArray> arrayConverter() {
        return ARRAY_CONVERTER;
    }

    @Override
    public Function<Object, String> stringConverter() {
        return STRING_CONVERTER;
    }

    @Override
    public Function<Object, Boolean> booleanConverter() {
        return BOOLEAN_CONVERTER;
    }

    @Override
    public Function<Object, Date> dateConverter() {
        return DATE_CONVERTER;
    }

    @Override
    public Function<Object, Integer> intConverter() {
        return INT_CONVERTER;
    }

    @Override
    public Function<Object, Long> longConverter() {
        return LONG_CONVERTER;
    }

    @Override
    public Function<Object, Double> doubleConverter() {
        return DOUBLE_CONVERTER;
    }

    @Override
    public <T extends Enum<T>> Function<Object, T> enumConverter(Class<T> clazz) {
        return o -> {
            Enum<?> anEnum = ENUM_CONVERTER.apply((Class<Enum<?>>) clazz, o);
            return (T) anEnum;
        };
    }

}
